local ffi = require("ffi");

ENDIANNESS_LITTLE = 0;
ENDIANNESS_BIG = 1;
ENDIANNESS_NATIVE = ENDIANNESS_LITTLE;

function bitswap(v,n,...)
    local args = {...};
    local r = 0;
    for i=0,n-1 do
        local pos = args[i+1];
        local vv = bit.band(bit.rshift(v,pos),1);
        r = bit.bor(r,bit.lshift(vv,n-i-1));
    end
    return r;
end

function ENDIAN_VALUE_LE_BE(endian,leval,beval)
    if(endian==ENDIANNESS_LITTLE)then
        return leval;
    else
        return beval;
    end
end

function NATIVE_ENDIAN_VALUE_LE_BE(leval,beval)
    return ENDIAN_VALUE_LE_BE(ENDIANNESS_NATIVE, leval, beval)
end

function BYTE_XOR_BE(v)
    return bit.bxor(v,NATIVE_ENDIAN_VALUE_LE_BE(1,0));
end

function BYTE_XOR_LE(v)
    return bit.bxor(v,NATIVE_ENDIAN_VALUE_LE_BE(0,1));
end


function mslug5_encryption_68k(rom,size)

    local xor1 = { --[[0xc2,]] 0x4b, 0x74, 0xfd, 0x0b, 0x34, 0xeb, 0xd7, 0x10, 0x6d, 0xf9, 0xce, 0x5d, 0xd5, 0x61, 0x29, 0xf5, 0xbe, 0x0d, 0x82, 0x72, 0x45, 0x0f, 0x24, 0xb3, 0x34, 0x1b, 0x99, 0xea, 0x09, 0xf3, 0x03 };
    local xor2 = { --[[0x36, ]]0x09, 0xb0, 0x64, 0x95, 0x0f, 0x90, 0x42, 0x6e, 0x0f, 0x30, 0xf6, 0xe5, 0x08, 0x30, 0x64, 0x08, 0x04, 0x00, 0x2f, 0x72, 0x09, 0xa0, 0x13, 0xc9, 0x0b, 0xa0, 0x3e, 0xc2, 0x00, 0x40, 0x2b };
    xor1[0] = 0xc2;
    xor2[0] = 0x36;

    local rom_size = 0x800000;
    local buf = ffi.new('uint8_t[?]',rom_size);

    --复制到目标缓存
    ffi.copy(buf,rom,rom_size);
    ffi.copy(buf+0x100000,rom+0x200000,0x600000);
    ffi.copy(buf+0x700000,rom+0x100000,0x100000);

    -- for i=0x100000,0x800000-1,0x100 do
    --     local ofst = bit.band(i,0xf000ff) + bit.bxor(bit.band(i,0x000f00),0x00700) +
    --         bit.lshift(bitswap(bit.rshift(bit.band(i,0x0ff000),12),8,5, 4, 7, 6, 1, 0, 3, 2),12);
    --     ffi.copy(rom+i,buf+ofst,0x100);
    -- end
    --把原rom的数据切换交换为基本数据
    --for i=0x800000-0x100,0x100000,-0x100 do

    --这是没有交换映射rom的数据
    ffi.copy(rom,buf,rom_size);

    for i=0x100000,0x800000-1,0x100 do
        local ofst = bit.band(i,0xf000ff) + bit.bxor(bit.band(i,0x000f00),0x00700) +
            bit.lshift(bitswap(bit.rshift(bit.band(i,0x0ff000),12),8,5, 4, 7, 6, 1, 0, 3, 2),12);
        ffi.copy(buf+ofst,rom+i,0x100);
    end
    --[[
    以上代码，经过
    for i=0x100000,0x800000-1,0x100 do
        local ofst = bit.band(i,0xf000ff) + bit.bxor(bit.band(i,0x000f00),0x00700) +
            bit.lshift(bitswap(bit.rshift(bit.band(i,0x0ff000),12),8,5, 4, 7, 6, 1, 0, 3, 2),12);
        ffi.copy(rom+i,buf+ofst,0x100);
    end
    会转换成原代码
    ]]

    for i=16-1,0,-1 do
        local ofst = bit.band(i,0xf0) +
            bitswap(bit.band(i,0xf),8,7, 6, 5, 4, 1, 0, 3, 2);
        ffi.copy(buf+ofst*0x10000,rom+i*0x10000,0x10000);
    end

    --[[
    逆转换
   for i=0,16-1 do
        local ofst = bit.band(i,0xf0) +
            bitswap(bit.band(i,0xf),8,7, 6, 5, 4, 1, 0, 3, 2);
        ffi.copy(rom+i*0x10000,buf+ofst*0x10000,0x10000);
    end
    ]]
    ffi.copy(rom,buf,rom_size);
    --for i=0x0800000-4,0x100000,-4 do
    for i=0x100000,0x0800000-1,4 do
        local rom16 = bit.bor(rom[BYTE_XOR_LE(i+1)],bit.lshift(rom[BYTE_XOR_LE(i+2)],8));
        --                         15  14  13  12  11  10  9  8  7  6  5  4  3  2  1  0
        --rom16 = bitswap(rom16,16,15, 14, 13, 12, 10, 11, 8, 9, 6, 7, 4, 5, 3, 2, 1, 0);
        rom16 =   bitswap(rom16,16,15, 14, 13, 12, 10, 11, 8, 9, 6, 7, 4, 5, 3, 2, 1, 0);

        local v1 = bit.band(rom16,0xff);
        local v2 = bit.rshift(rom16,8);
        rom[BYTE_XOR_LE(i+1)] = v1;
        rom[BYTE_XOR_LE(i+2)] = v2;
    end

    --进行一次xor
    --for i=0x800000-1,0x100000 do
    for i=0x100000,0x800000-1 do
        local idx = BYTE_XOR_LE(i) % 0x20;
        local b = xor2[idx];
        --[[]
        rom[i] = bit.bxor(rom[i],b);
        ]]
        rom[i] = bit.bxor(rom[i],b);
    end

    for i=0,0x100000-1 do
        local v = BYTE_XOR_LE(i);
        rom[i] = bit.bxor(rom[i],xor1[(v % 0x20)]);
    end
end


assert(#arg>0,"no file");
local file = io.open(arg[1],"rb");
assert(file,"file open error")
local data = file:read("*a");
local rom = ffi.new('uint8_t[?]',#data);
ffi.copy(rom,data);
--print(rom[1]);
--print(ffi.sizeof(rom));

--mslug5_en_68k(rom,#data);
mslug5_encryption_68k(rom,#data);
local out = arg[1]..'.d';
local file = io.open(out,"wb");
file:write(ffi.string(rom,#data));
file:close();

